package com.simplenix.nicasio.misc;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

public class DateUtil {

	/**
	 * Zera o hor?rio de um objeto Date
	 * @param date
	 * 		Data para o hor?rio ser zerado
	 * @return
	 */
	public static Date zeraHorario(Date date) {
		if (date == null) {
			return null;
		}
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal = zeraHorario(cal);
		return cal.getTime();
	}

	public static Date hojeSemHorario() {
		return zeraHorario(new Date());
	}

	/**
	 * Zera o hor?rio de um objeto Calendar
	 * @param cal
	 * 		Calendar para o hor?rio ser zerado
	 * @return
	 */
	public static Calendar zeraHorario(Calendar cal) {
		if (cal == null) {
			return null;
		}
		cal.set(Calendar.HOUR_OF_DAY, 0);
		return zeraMinutos(cal);
	}

	/**
	 * Zera os minutos de um tipo Date
	 * @param date
	 * 		Data para os minutos serem zerado
	 * @return
	 */
	public static Date zeraMinutos(Date date) {
		if (date == null) {
			return null;
		}
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal = zeraMinutos(cal);
		return cal.getTime();
	}

	/**
	 * Zera os minutos de um tipo Calendar
	 * @param date
	 * 		Data para os minutos serem zerados
	 * @return
	 */
	public static Calendar zeraMinutos(Calendar cal) {
		if (cal == null) {
			return null;
		}
		cal.set(Calendar.MINUTE, 0);
		return zeraSegundos(cal);
	}

	public static Date zeraSegundos(Date date) {
		if (date == null) {
			return null;
		}
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal = zeraSegundos(cal);
		return cal.getTime();
	}

	public static Calendar zeraSegundos(Calendar cal) {
		if (cal == null) {
			return null;
		}
		cal.set(Calendar.SECOND, 0);
		return zeraMiliSegundos(cal);
	}

	public static Date zeraMiliSegundos(Date date) {
		if (date == null) {
			return null;
		}
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal = zeraMiliSegundos(cal);
		return cal.getTime();
	}

	public static Calendar zeraMiliSegundos(Calendar cal) {
		if (cal == null) {
			return null;
		}
		cal.set(Calendar.MILLISECOND, 0);
		return cal;
	}

	public static String getDiaSemana(int diaSemana) {
		if (diaSemana == Calendar.MONDAY) {
			return "Segunda";
		} else if (diaSemana == Calendar.TUESDAY) {
			return "Ter?a";
		} else if (diaSemana == Calendar.WEDNESDAY) {
			return "Quarta";
		} else if (diaSemana == Calendar.THURSDAY) {
			return "Quinta";
		} else if (diaSemana == Calendar.FRIDAY) {
			return "Sexta";
		} else if (diaSemana == Calendar.SATURDAY) {
			return "S?bado";
		} else if (diaSemana == Calendar.SUNDAY) {
			return "Domingo";
		}
		return null;
	}

	public static String getDiaSemanaAbrev(int diaSemana) {
		if (diaSemana == Calendar.MONDAY) {
			return "Seg";
		} else if (diaSemana == Calendar.TUESDAY) {
			return "Ter";
		} else if (diaSemana == Calendar.WEDNESDAY) {
			return "Qua";
		} else if (diaSemana == Calendar.THURSDAY) {
			return "Qui";
		} else if (diaSemana == Calendar.FRIDAY) {
			return "Sex";
		} else if (diaSemana == Calendar.SATURDAY) {
			return "Sab";
		} else if (diaSemana == Calendar.SUNDAY) {
			return "Dom";
		}
		return null;
	}

	public static String getMes(int mes) {
		if (mes == Calendar.JANUARY) {
			return "Janeiro";
		} else if (mes == Calendar.FEBRUARY) {
			return "Fevereiro";
		} else if (mes == Calendar.MARCH) {
			return "Mar?o";
		} else if (mes == Calendar.APRIL) {
			return "Abril";
		} else if (mes == Calendar.MAY) {
			return "Maio";
		} else if (mes == Calendar.JUNE) {
			return "Junho";
		} else if (mes == Calendar.JULY) {
			return "Julho";
		} else if (mes == Calendar.AUGUST) {
			return "Agosto";
		} else if (mes == Calendar.SEPTEMBER) {
			return "Setembro";
		} else if (mes == Calendar.OCTOBER) {
			return "Outubro";
		} else if (mes == Calendar.NOVEMBER) {
			return "Novembro";
		} else if (mes == Calendar.DECEMBER) {
			return "Dezembro";
		}
		return null;
	}

	public static Date somaDias(Date date, int dias) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.DAY_OF_MONTH, dias);
		return cal.getTime();
	}

	public static Date somaMeses(Date date, int meses) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.MONTH, meses);
		return cal.getTime();
	}

	public static Date somaAnos(Date date, int anos) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.YEAR, anos);
		return cal.getTime();
	}

	public static Date somaMinutos(Date date, int minutos) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.MINUTE, minutos);
		return cal.getTime();
	}

	public static Date getDataMinima() {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.MONTH, Calendar.JANUARY);
		cal.set(Calendar.YEAR, 1980);
		cal.set(Calendar.HOUR, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTime();
	}

	public static Date getDataMaxima() {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.MONTH, Calendar.JANUARY);
		cal.set(Calendar.YEAR, 2030);
		cal.set(Calendar.HOUR, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTime();
	}

	/**
	 * Calcula o numero de dias entre duas datas, sendo que data tem que ser
	 * maior que dt2
	 * 
	 * @param dt1
	 * @param dt2
	 * @return
	 */
	public static int calculaNumeroDias(Date dt1, Date dt2) {
		Date d1 = zeraHorario(dt1);
		Date d2 = zeraHorario(dt2);

		if (dt1 == null || dt2 == null) {
			return 0;
		}

		if (d2.compareTo(d1) < 0) {
			return 0;
		}

		if (d2.compareTo(d1) == 0) {
			return 1;
		}
		int i;
		i = -1;
		for (long mdate = d1.getTime(); mdate <= d2.getTime(); mdate = mdate + 86400000) {
			i++;
		}
		return i + 1;
	}

	/**
	 * Retorna a maior data entre as datas informadas
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
	public static Date max(Date d1, Date d2) {
		if (d1.compareTo(d2) > 0) {
			return d1;
		}
		return d2;
	}

	/**
	 * Retorna a maior data da lista informada
	 * 
	 * @param datas
	 *            lista de datas
	 * @return
	 */
	public static Date max(List datas) {
		Date maxDate = null;
		for (Iterator itData = datas.iterator(); itData.hasNext();) {
			Date data = (Date) itData.next();
			if (data != null) {
				if (maxDate == null) {
					maxDate = data;
				} else {
					maxDate = max(maxDate, data);
				}
			}
		}
		return maxDate;
	}

	public static long dataPadraoUnix(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		return cal.getTimeInMillis();
	}

	/**
	 * Retorna a menor data entre as datas informadas
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
	public static Date min(Date d1, Date d2) {
		if (d1.compareTo(d2) > 0) {
			return d2;
		}
		return d1;
	}

	/**
	 * Retorna a menor data da lista informada
	 * 
	 * @param datas
	 *            lista de datas
	 * @return
	 */
	public static Date min(List datas) {
		Date minDate = null;
		for (Iterator itData = datas.iterator(); itData.hasNext();) {
			Date data = (Date) itData.next();
			if (data != null) {
				if (minDate == null) {
					minDate = data;
				} else {
					minDate = min(minDate, data);
				}
			}
		}
		return minDate;
	}

	public static boolean isFimDeSemana(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		int diaSemana = cal.get(Calendar.DAY_OF_WEEK);
		if (diaSemana == Calendar.SATURDAY) {
			return true;
		} else if (diaSemana == Calendar.SUNDAY) {
			return true;
		}
		return false;
	}

	/**
	 * Verifica se a data informada ? feriado.
	 * 
	 * @param date
	 *            data para testar se ? feriado ou n?o
	 * @param feriados
	 *            lista de datas que s?o feriado
	 * @return
	 */
	public static boolean isFeriado(Date date, List feriados) {
		for (Iterator it = feriados.iterator(); it.hasNext();) {
			Date feriado = (Date) it.next();
			if (isFeriado(date, feriado)) {
				return true;
			}
		}
		return false;
	}

	private static boolean isFeriado(Date date, Date feriado) {
		boolean rtn = false;
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		Calendar cfer = Calendar.getInstance();
		cfer.setTime(feriado);
		// Nao utilizo o Calendar.DAY_OF_YEAR por causa do ANO BISSEXTO.
		if (c.get(Calendar.DAY_OF_MONTH) == cfer.get(Calendar.DAY_OF_MONTH) && c.get(Calendar.MONTH) == cfer.get(Calendar.MONTH)) {
			rtn = true;
		}
		return rtn;
	}

	/**
	 * Formata uma data no padr?o do sistema dd/MM
	 * 
	 * @param date
	 * @return
	 */
	public static String formatPadraoDiaMes(Date date) {
		SimpleDateFormat df = new SimpleDateFormat("dd/MM");
		if (date != null) {
			return df.format(date);
		}
		return "";
	}

	/**
	 * Formata uma data no padr?o do sistema dd/MM/yyyy
	 * 
	 * @param date
	 * @return
	 */
	public static String formatPadrao(Date date) {
		SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
		if (date != null) {
			return df.format(date);
		}
		return "";
	}

	/**
	 * Faz o parse de uma data formatada no padrao do sistema dd/MM/yyyy
	 * 
	 * @param date
	 * @return
	 */
	public static Date parsePadrao(String date) throws ParseException {
		if (!StrUtil.nvl(date).equals("")) {
			SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
			return df.parse(date);
		}
		return null;
	}

	/**
	 * Faz o parse de uma data formatada no padrao do sistema dd/MM/yyyy HH:mm
	 * 
	 * @param date
	 * @return
	 */
	public static Date parsePadraoDataHora(String date) throws ParseException {
		if (!StrUtil.nvl(date).equals("")) {
			SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm");
			return df.parse(date);
		}
		return null;
	}

	/**
	 * Formata uma data no padrao DIA de MES de ANO
	 * 
	 * @param date
	 * @return
	 */
	public static String formatLonga(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		int dia = cal.get(Calendar.DAY_OF_MONTH);
		int mes = cal.get(Calendar.MONTH);
		int ano = cal.get(Calendar.YEAR);
		return dia + " de " + getMes(mes) + " de " + ano;
	}

	/**
	 * Formata uma data no padr?o <code>DiaSemana(Tres caracteres) DD/MM/AAAA</code>
	 */
	public static String formatComDiaDaSemana(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		String ds = getDiaSemanaAbrev(cal.get(Calendar.DAY_OF_WEEK));
		return ds + " " + formatPadrao(cal.getTime());
	}

	/**
	 * Formata uma data no padr?o DIA/MES/ANO HORA:MINUTO:SEGUNDO
	 * 
	 * @param date
	 * @return
	 */
	public static String formatDetalhada(Date date) {
		SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
		return df.format(date);
	}

	/*
	 * Calcula a diferenca em dias entre duas datas
	 */
	public static long calculaDiferencaEmDias(Date dt1, Date dt2) {

		if (dt1 == null || dt2 == null) {
			return 0;
		}

		long dias = zeraHorario(dt1).getTime() - zeraHorario(dt2).getTime();
		dias = dias / 86400000;
		return dias;

	}

	/**
	 * Formata no padrao o mais curto possivel
	 * 
	 * @param date
	 * @return
	 */
	public static String formatCurto(Date pdate) {
		SimpleDateFormat df = new SimpleDateFormat("dd/MM");
		if (pdate != null) {
			return df.format(pdate);
		}
		return "";
	}

	public static Date getPrimeiroDiaDoMes(Date pdata) throws ParseException {
		SimpleDateFormat df = new SimpleDateFormat("MM/yyyy");
		String dia1 = "";
		if (pdata != null) {
			dia1 = "01/" + df.format(pdata);
		}
		return parsePadrao(dia1);
	}

	public static Date getUltimoDiaDoMes(Date pdata) throws ParseException {
		Date aux;

		aux = pdata;
		aux = somaMeses(aux, 1);
		aux = getPrimeiroDiaDoMes(aux);
		aux = somaDias(aux, -1);

		return aux;

	}

	public static String removePrefixoDias(String strData) {
		strData = strData.replaceAll("Seg", "");
		strData = strData.replaceAll("Ter", "");
		strData = strData.replaceAll("Qua", "");
		strData = strData.replaceAll("Qui", "");
		strData = strData.replaceAll("Sex", "");
		strData = strData.replaceAll("Sab", "");
		strData = strData.replaceAll("Dom", "");
		strData = strData.trim();
		return strData;
	}

	public static List<Date> limitaDatas(Date dtInicio, Date dtFim, BigDecimal limite) throws Exception {
		List<Date> rtn = new ArrayList();

		List<Date> dias = getDiasNoIntervalo(dtInicio, dtFim);

		//Pega o primeiro dia
		rtn.add(dias.get(0));
		//Como adicionou o primeiro dia, o limite para trabalho daqui para frente ? o limite informado - 1
		limite = limite.subtract(new BigDecimal(1));

		BigDecimal nrDias = new BigDecimal(dias.size());
		if (limite.compareTo(nrDias) < 0) {
			BigDecimal escala = nrDias.divide(limite, 0, BigDecimal.ROUND_HALF_UP).setScale(0, BigDecimal.ROUND_HALF_UP);
			BigDecimal diaDoMeio = nrDias.divide(new BigDecimal(2), 0, BigDecimal.ROUND_HALF_UP).setScale(0, BigDecimal.ROUND_HALF_UP);
			//Pega dia do meio
			rtn.add(dias.get(diaDoMeio.intValue()));
			//Come?a do primeiro dia v?lido na escala
			for (int i = escala.intValue(); i <= limite.intValue();) {

				if (i % 2 == 0) {
					if (!rtn.contains(dias.get(i))) {
						rtn.add(dias.get(i));
					} else {
						i++;
					}
				} else {
					if (!rtn.contains(dias.get(dias.size() - (1 + i)))) {
						rtn.add(dias.get(dias.size() - (1 + i)));
					} else {
						i++;
					}
				}
			}
			//Pega o ?ltimo dia
			if (!rtn.contains(dias.get(dias.size() - 1))) {
				rtn.add(dias.get(dias.size() - 1));
			}
		} else {
			for (int i = 1; i <= dias.size() - 1; i++) {
				rtn.add(dias.get(i));
			}
		}
		//Por desencargo de consci?ncia... ordena
		Collections.sort(rtn);
		return rtn;
	}

	public static List<Date> getDiasNoIntervalo(Date dtInicio, Date dtFim) throws Exception {
		List<Date> rtn = new ArrayList();
		if (dtInicio.compareTo(dtFim) <= 0) {
			while (dtInicio.compareTo(dtFim) <= 0) {
				rtn.add(dtInicio);
				dtInicio = DateUtil.somaDias(dtInicio, 1);
			}
		} else {
			throw new Exception("Data de Fim nao pode ser menor que a data de Inicio.");
		}
		return rtn;
	}

	public static boolean isInRange(Date ponto, Date inicio, Date fim) {
		if (inicio.compareTo(ponto) <= 0 && fim.compareTo(ponto) >= 0) {
			return true;
		} else {
			return false;
		}
	}

}
